/**
 * \file Value.cpp
 * \brief Implementation of known value Expression subclasses
 * \author Wouter Caarls <w.caarls@tnw.tudelft.nl> 
 *
 * \verbatim
 * Copyright (c) 2005 Wouter Caarls, Delft University of Technology
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 * \endverbatim
 */

#include "Base.h"
#include "Code.h"
#include "Expression.h"
#include "LeftExpression.h"
#include "Symbol.h"
#include "Value.h"
#include "LeftExpression.h"

#include "assert.h"

VoidValue* VoidValue::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  return copy();
}

std::string VoidValue::toString(int indentation)
{
  return (char*)(CStr(indent(indentation)) << "");
}  

std::string VoidValue::toTerm()
{
  return (char*)CStr("EmptyExp");
}  

int VoidValue::castToInteger() const
{
  plog.warning() << HDR << "cannot cast void to integer" << std::endl;
  return 0;
}

double VoidValue::castToFloat() const
{
  plog.warning() << HDR << "cannot cast void to float" << std::endl;
  return 0;
}

/* ************************************************************************* */

IntegerValue* IntegerValue::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  return copy();
}

std::string IntegerValue::toString(int indentation)
{
  if (known)
  {
    char buf[100];
    sprintf(buf, "%d", data);
    
    return (char*)(CStr(indent(indentation)) << buf);
  }
  else
    return (char*)(CStr(indent(indentation)) << "?");
}  

std::string IntegerValue::toTerm()
{
  if (data >= 0)
    return (char*)(CStr("IntConst(\"") << data << "\")");
  else
    return (char*)(CStr("Negative(IntConst(\"") << -data << "\"))");
}  

int IntegerValue::castToInteger() const 
{
  if (!known) error("cannot cast unknown value");
  return data;
}

double IntegerValue::castToFloat() const
{
  if (!known) error("cannot cast unknown value");
  return data;
}

void IntegerValue::outputKnownValues(LeftExpression *base, Compound *out)
{
  if (known)
    out->push_back_stat(new Stat(new Assign(type, AssignEq, base->copy(), copy())));
}

/* ************************************************************************* */

FloatValue* FloatValue::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  return copy();
}

std::string FloatValue::toString(int indentation)
{
  if (known)
  {
    char buf[100];
    sprintf(buf, "%e", data);
    return (char*)(CStr(indent(indentation)) << buf);
  }
  else
    return (char*)(CStr(indent(indentation)) << "?");
}

std::string FloatValue::toTerm()
{
  if (data >= 0)
    return (char*)(CStr("FloatConst(\"") << data << "\")");
  else
    return (char*)(CStr("Negative(FloatConst(\"") << -data << "\"))");
}  

int FloatValue::castToInteger() const 
{
  if (!known) error("cannot cast unknown value");
  return (int)data;
}

double FloatValue::castToFloat() const
{
  if (!known) error("cannot cast unknown value");
  return data;
}

void FloatValue::outputKnownValues(LeftExpression *base, Compound *out)
{
  if (known)
    out->push_back_stat(new Stat(new Assign(type, AssignEq, base->copy(), copy())));
}

/* ************************************************************************* */

PointerValue::PointerValue() : Value(&NullPointer)
{
  ptr_to_t _to;

  _to.index = 0;
  _to.route = new Id(&NullPointer, "__invalid_pointer__");

  to.push_back(_to);
}

PointerValue::PointerValue(SymbolTable *symbols, int _index, LeftExpression *_route) :
  Value(&NullPointer)
{
  LOG(plog.debug, HDR << "creating pointer " << this << " at index " << _index << std::endl);

  ptr_to_t _to;

  _to.index = _index;
  _to.route = _route;

  to.push_back(_to);  

  Variable *var = dynamic_cast<Variable*>(symbols->getSymbol(_index));

  if (!var)
  {
    type = &NullPointer;
    plog.warning() << HDR << "creating invalid pointer" << std::endl;
  }

  std::vector<PointerOf*> povect;
  povect.push_back(new PointerOf());
  type = constructType(_route->type, povect, symbols);
  delete povect[0];
}

PointerValue *PointerValue::copy()
{
  PointerValue *res;

  res = new PointerValue(type, to, assignments, declarations);
  res->known = known;

  return res;
}

Value* PointerValue::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  if (use == AsCode && type == to[0].route->type && storageIsKnown(symbols, out, true))
  {
    /* Reduce pointer to array as code */
    std::list<Value**> storage, path;
    LeftExpression *anonroute = to[0].route->anonimizeStoragePath(to[0].index);
    anonroute->findStorage(symbols, out, storage, path);
    assert(storage.size() == 1);

    return (*storage.front())->copy();
  }
  else
    return this->copy();
}

void PointerValue::residualize(SymbolTable *symbols, bool markUnknown)
{
  Compound *dummy = new Compound();

  plog.residualize() << HDR << "residualizing pointer value " << toString() << std::endl;

  Value::residualize(symbols, markUnknown);

  if (!known)
    return;

  for (TI ti = to.begin(); ti != to.end(); ++ti)
  {
    LeftExpression *anonroute = ti->route->anonimizeStoragePath(ti->index);
    std::list<Value**> storage, path;
    anonroute->findStorage(symbols, dummy, storage, path);

    delete anonroute;

    LOG(plog.crawl, HDR << "residualizing storage" << std::endl);

    if (!storage.empty())
    {
      LeftExpression::SI ii;

      for (ii = storage.begin(); ii != storage.end(); ++ii)
	(**ii)->residualize(symbols, markUnknown);
    }  
  }

  LOG(plog.crawl, HDR << "done residualizing" << std::endl);

  delete dummy;
}

bool PointerValue::dirtyChanged(SymbolTable *symbols, Value *rhs, bool mergeAssignments)
{
  if (typeid(*this) != typeid(*rhs))
  {
    plog.err() << HDR << "Merging incompatible values " << typeid(*this).name() << " vs "
	       << typeid(*rhs).name() << std::endl;
    exit(-1);
  }

  LOG(plog.crawl, HDR << "PointerValue::dirtyChanged " << known << " " << rhs->known << std::endl);
  LOG(plog.crawl, HDR << "PointerValue::dirtyChanged " << this << " " << rhs << std::endl);

  if (known && rhs->known)
  {   
    /* If both pointers are known, add rhs' distinctiveness to our own */
    bool added = false;

    PointerValue *prhs = dynamic_cast<PointerValue*>(rhs);
    for (TI ti=prhs->to.begin(); ti != prhs->to.end(); ++ti)
    {
      bool found = false;

      for (TI ti2=to.begin(); ti2 != to.end(); ++ti2)
	if (ti2->index == ti->index &&
	    ti2->route->equals(ti->route))
	{
	  found = true;
	  break;
	}

      if (found == false)
      {
	ptr_to_t newto;
	
	newto.index = ti->index;
	newto.route = ti->route->copy();
	
	to.push_back(newto);
	added = true;

	LOG(plog.crawl, HDR << "added " << ti->route->toString() << std::endl);
      }
    }
  }
  else
  {
    /* If any of the pointers isn't known, the result can't be known. */
    if (!to.empty())
      for (TI ti=to.begin(); ti != to.end(); ++ti)
	delete ti->route;
    
    to.clear();
    known = false;
  }

  /* Pointers don't become unknown if they have multiple assignments */
  bool _known = known;
  bool retval = Value::dirtyChanged(symbols, rhs, mergeAssignments);
  known = _known;

  if (!to.empty())
    for (TI ti=to.begin(); ti != to.end(); ++ti)
    {
      LOG(plog.crawl, HDR << "now sporting path " << ti->route->toString() << std::endl);
    }

  return retval;
}

std::string PointerValue::toString(int indentation)
{
  /* Normally, type == constructType(route->type, 1), but if this pointer is
   * actually pointing to an array, they may be the same. In that case, the
   * "&" should not be printed or passed to strategies
   */

  if (known && to.size() == 1)
  {
    LOG(plog.crawl, HDR << "type " << type->toString() << " route->type " << to[0].route->type->toString() << std::endl);

    if (type == to[0].route->type)
      return (char*)(CStr(indent(indentation)) << to[0].route->toString());
    else
      return (char*)(CStr(indent(indentation)) << "&(" << to[0].route->toString() << ")");
  }
  else if (known)
  {
    CStr res;

    res << indent(indentation) << "(";
    for (TI ti=to.begin(); ti != to.end(); ++ti)
    {
      if (type != ti->route->type)
	res << "&(";

      res << ti->route->toString();

      if (type != ti->route->type)
	res << ")";
      
      if (ti+1 != to.end())
	res << ", ";
    }
    res << ")";

    return (char*)res;
  }
  else
    return (char*)(CStr(indent(indentation)) << "?");
}

std::string PointerValue::toTerm()
{
  if (to.size() == 1)
  {
    if (type == to[0].route->type)
      return to[0].route->toTerm();
    else
      return (char*)(CStr("Address(") << to[0].route->toTerm() << ")");
  }
  else
    return "UnknownPointer";
}

int PointerValue::castToInteger() const 
{
  plog.warning() << HDR << "cannot cast pointer to integer" << std::endl;
  return 0;
}

double PointerValue::castToFloat() const
{
  plog.warning() << HDR << "cannot cast pointer to float" << std::endl;
  return 0;
}

bool PointerValue::storageIsKnown(SymbolTable *symbols, Compound *out, bool useValue)
{
  LOG(plog.debug, HDR << this << std::endl);

  if (!known) return false;
  if (to.size() != 1) return false;

  LeftExpression *anonroute = to[0].route->anonimizeStoragePath(to[0].index);
  std::list<Value**> storage, path;
  anonroute->findStorage(symbols, out, storage, path);

  delete anonroute;

  if (storage.size() == 1)
  {
    if (useValue)
      return (*storage.front())->storageIsKnown(symbols, out, useValue);
    else
      return true;
  }
  else
    return false;
}

/* ************************************************************************* */

ArrayValue *ArrayValue::copy()
{
  ArrayValue *res = new ArrayValue(type, assignments, declarations);

  for (EI ii=data.begin(); ii != data.end(); ++ii)
    res->data.push_back(new Value*((**ii)->copy()));

  res->known = known;

  return res;
}

ArrayValue* ArrayValue::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  return this->copy();
}

void ArrayValue::residualize(SymbolTable *symbols, bool markUnknown)
{
  Value::residualize(symbols, markUnknown);

  for (EI ii=data.begin(); ii != data.end(); ++ii)
    (**ii)->residualize(symbols, markUnknown);
}

void ArrayValue::removeGenerators()
{
  Value::removeGenerators();

  for (EI ii=data.begin(); ii != data.end(); ++ii)
    (**ii)->removeGenerators();
}

bool ArrayValue::dirtyChanged(SymbolTable *symbols, Value *rhs, bool mergeAssignments)
{
  if (typeid(*this) != typeid(*rhs))
  {
    plog.err() << HDR << "Merging incompatible values " << typeid(*this).name() << " vs "
	       << typeid(*rhs).name() << std::endl;
    exit(-1);
  }

  ArrayValue *arhs = dynamic_cast<ArrayValue*>(rhs);
  EI ii=data.begin(), jj=arhs->data.begin();

  for (; ii != data.end() && jj != arhs->data.end(); ++ii, ++jj)
    (**ii)->dirtyChanged(symbols, (**jj), mergeAssignments);

  return false;
}

std::string ArrayValue::toString(int indentation)
{
  CStr res;

  if (known)
  {
    PointerType *ptype = dynamic_cast<PointerType*>(type);
    if (!ptype)
      error("Internal type mismatch");

    if (ptype->elements > 0)
    {
      IntegerType *itype = dynamic_cast<IntegerType*>(ptype->to);
      IntegerValue *terminator = dynamic_cast<IntegerValue*>(*data[ptype->elements-1]);

      if (itype && itype->precision == 8 && terminator && !terminator->data)
      {
	/* Null-terminated string */
	
	res << "\"";
	for (EI ii=data.begin(); ii+1 != data.end(); ++ii)
	{
	  IntegerValue *element = dynamic_cast<IntegerValue*>(**ii);
	  
	  if (!element)
	    error("Internal type mismatch");

	  res.put(element->data);
	}
	res << "\"";

	return (char*)res;
      }
    }

    res << indent(indentation) << "{";
    for (EI ii=data.begin(); ii != data.end(); ++ii)
    {
      res << (**ii)->toString();
      if (ii != data.end()-1)
	res << ", ";
    }
    res << "}";
  }
  else
    res << "" << indent(indentation) << "?";

  return (char*)res;
}

std::string ArrayValue::toTerm()
{
  CStr res;

  PointerType *ptype = dynamic_cast<PointerType*>(type);
  if (!ptype)
    error("Internal type mismatch");
  
  IntegerType *itype = dynamic_cast<IntegerType*>(ptype->to);
  
  if (itype && itype->precision == 8)
  {
    std::string str = toString();

    str.erase(str.begin());
    str.erase(str.end()-1);

    res << "StringLit([\"\\\"";

    for (unsigned int ii=0; ii != str.length(); ++ii)
    {
      if (str[ii] == '\\' || str[ii] == '\"')
	res.put('\\');
      res.put(str[ii]);
    }

    res << "\\\"\"])";
  }
  else
  {
    res << "ArrayInit([";
    for (EI ii=data.begin(); ii != data.end(); ++ii)
    {
      res << "AssignInit(" << (**ii)->toTerm() << ")";
      if (ii != data.end()-1)
	res << ", ";
    }
    res << "])";
  }

  return (char*)res;
}

int ArrayValue::castToInteger() const 
{
  plog.warning() << HDR << "cannot cast array to integer" << std::endl;
  return 0;
}

double ArrayValue::castToFloat() const
{
  plog.warning() << HDR << "cannot cast array to float" << std::endl;
  return 0;
}

void ArrayValue::outputKnownValues(LeftExpression *base, Compound *out)
{
  PointerType *ptype = dynamic_cast<PointerType*>(type);

  if (known)
  {
    for (unsigned int ii=0; ii != data.size(); ++ii)
    {
      ArrayIndex *idx = new ArrayIndex(ptype->to, base->copy(), new IntegerValue(ii));
      (*data[ii])->outputKnownValues(idx, out);
      delete idx;
    }
  }
}

bool ArrayValue::storageIsKnown(SymbolTable *symbols, Compound *out, bool useValue)
{
  bool subknown = known;

  for (EI ii=data.begin(); ii != data.end(); ++ii)
    subknown &= (**ii)->storageIsKnown(symbols, out, useValue);

  return subknown;
}

/* ************************************************************************* */

StructValue *StructValue::copy()
{
  StructValue *res = new StructValue(type, assignments, declarations);

  for (EI ii=data.begin(); ii != data.end(); ++ii)
    res->data.push_back(new Value*((**ii)->copy()));

  res->known = known;

  return res;
}

StructValue* StructValue::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  return this->copy();
}

void StructValue::residualize(SymbolTable *symbols, bool markUnknown)
{
  Value::residualize(symbols, markUnknown);

  for (EI ii=data.begin(); ii != data.end(); ++ii)
    (**ii)->residualize(symbols, markUnknown);
}

void StructValue::removeGenerators()
{
  Value::removeGenerators();

  for (EI ii=data.begin(); ii != data.end(); ++ii)
    (**ii)->removeGenerators();
}

bool StructValue::dirtyChanged(SymbolTable *symbols, Value *rhs, bool mergeAssignments)
{
  bool retval=false;

  if (typeid(*this) != typeid(*rhs))
  {
    plog.err() << HDR << "Merging incompatible values " << typeid(*this).name() << " vs "
	       << typeid(*rhs).name() << std::endl;
    exit(-1);
  }

  if (mergeAssignments && (retval=Value::dirtyChanged(symbols, rhs, mergeAssignments)))
  {
    LOG(plog.crawl, HDR << "Merging down assignments" << std::endl);
    for (EI ii=data.begin(); ii != data.end(); ++ii)
      (**ii)->mergeAssignments(assignments);
  }

  StructValue *srhs = dynamic_cast<StructValue*>(rhs);
  EI ii=data.begin(), jj=srhs->data.begin();
  
  for (; ii != data.end() && jj != srhs->data.end(); ++ii, ++jj)
    (**ii)->dirtyChanged(symbols, (**jj));
  
  return retval;
}

std::string StructValue::toString(int indentation)
{
  CStr res;

  if (known)
  {
    res << indent(indentation) << "{";
    for (EI ii=data.begin(); ii != data.end(); ++ii)
    {
      res << (**ii)->toString();
      if (ii != data.end()-1)
	res << ", ";
    }
    res << "}";
  }
  else
    res << indent(indentation) << "?";

  return (char*)res;
}

std::string StructValue::toTerm()
{
  CStr res;

  res << "ArrayInit([";
  for (EI ii=data.begin(); ii != data.end(); ++ii)
  {
    res << "AssignInit(" << (**ii)->toTerm() << ")";
    if (ii != data.end()-1)
      res << ", ";
  }
  res << "])";

  return (char*)res;
}

int StructValue::castToInteger() const 
{
  plog.warning() << HDR << "cannot cast struct to integer" << std::endl;
  return 0;
}

double StructValue::castToFloat() const
{
  plog.warning() << HDR << "cannot cast struct to float" << std::endl;
  return 0;
}

void StructValue::outputKnownValues(LeftExpression *base, Compound *out)
{
  StructType *stype = dynamic_cast<StructType*>(type);

  if (known)
  {
    for (unsigned int ii=0; ii != data.size(); ++ii)
    {
      Field *fld = new Field(stype->subtypes[ii]->type, base->copy(),
			     new Id(&Unknown, stype->subtypes[ii]->name));
      (*data[ii])->outputKnownValues(fld, out);
      delete fld;
    }
  }
}

bool StructValue::storageIsKnown(SymbolTable *symbols, Compound *out, bool useValue)
{
  bool subknown = known;

  for (EI ii=data.begin(); ii != data.end(); ++ii)
    subknown &= (**ii)->storageIsKnown(symbols, out, useValue);

  return subknown;
}

/* ************************************************************************* */

Expression* CodeValue::reduce(SymbolTable *symbols, Compound *out, Use use)
{
  if (!data)
    return new CodeValue();

  Escape *esc = new Escape();
  CodeValue *res = new CodeValue(data->replace(esc, NULL, symbols, out));
  delete esc;
  
  return res;
}

std::string CodeValue::toString(int indentation)
{
  if (known)
  {
    if (data)
      return (char*)(CStr(indent(indentation)) << "`" << data->toString() << "`");
    else
      return "``";
  }
  else
    return (char*)(CStr(indent(indentation)) << "?");
}  

std::string CodeValue::toTerm()
{
  if (data)
    return (char*)(CStr("CodeConst(") << data->toTerm() << ")");
  else
    return "CodeConst(EmptyExp)";
}  

int CodeValue::castToInteger() const
{
  plog.warning() << HDR << "cannot cast code to float" << std::endl;
  return 0;
}

double CodeValue::castToFloat() const
{
  plog.warning() << HDR << "cannot cast code to float" << std::endl;
  return 0;
}

void CodeValue::outputKnownValues(LeftExpression *base, Compound *out)
{
  if (known)
    out->push_back_stat(new Stat(new Assign(type, AssignEq, base->copy(), copy())));
}
